/*-
 * Copyright (c) 2008-2010, Oleg Estekhin
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  * Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the distribution.
 *  * Neither the names of the copyright holders nor the names of their
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 */

package oe.assertions.predicates;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import oe.assertions.MessageBuffer;
import oe.assertions.Predicate;

/**
 * A composite predicate contains other predicates. The result of the composite operator is determined by results of
 * contained predicates and an operator used to combine them.
 */
public abstract class CompositePredicate<T> implements Predicate<T> {

    private final CompositeOperator operator;

    private final List<Predicate<? super T>> predicates;


    /**
     * Creates a new composite predicate.
     *
     * @param operator the operator
     * @param predicates a list of predicates
     *
     * @throws IllegalArgumentException if {@code operator} is {@code null}
     * @throws IllegalArgumentException if {@code predicates} is {@code null}, or empty, or contains {@code null}
     * element
     */
    protected CompositePredicate( CompositeOperator operator, List<Predicate<? super T>> predicates ) {
        if ( operator == null ) {
            throw new IllegalArgumentException( "operator is null" );
        }
        if ( predicates == null ) {
            throw new IllegalArgumentException( "predicates is null" );
        }
        if ( predicates.isEmpty() ) {
            throw new IllegalArgumentException( "predicates is empty" );
        }
        if ( predicates.contains( null ) ) {
            throw new IllegalArgumentException( "predicates contains null" );
        }
        this.operator = operator;
        this.predicates = Collections.unmodifiableList( new ArrayList<Predicate<? super T>>( predicates ) );
    }


    @Override
    public boolean check( T input, MessageBuffer message ) {
        MessageBuffer compositeMessage = new MessageBuffer( message, message.getIndentationLevel() + 1 );
        switch ( operator ) {
            case ALL_OF:
                compositeMessage.indent().append( "to satisfy all of:" ).eol();
                break;
            case ANY_OF:
                compositeMessage.indent().append( "to satisfy any of:" ).eol();
                break;
            default:
                throw new AssertionError( operator );
        }
        boolean compositeResult = operator.initial();
        for ( Predicate<? super T> predicate : predicates ) {
            // forced examination of all predicates in order to collect all failed assertions
            compositeResult = operator.combine( compositeResult, predicate.check( input, compositeMessage ) );
        }
        if ( !compositeResult ) {
            compositeMessage.publish();
        }
        compositeMessage.dispose();
        return compositeResult;
    }

}
